/*实现异步工作器*/
#ifndef __LOOPER_HPP__
#define __LOOPER_HPP__

#include"buffer.hpp"
#include<mutex>
#include<thread>
#include<condition_variable>
#include<functional>
#include<memory>
#include<atomic>
namespace kaizi
{
    using Functor=std::function<void(Buffer&)>;//回调函数类型
    enum class AsyncType
    {
        ASYNC_SAFE,//安全状态,表示缓冲区满了则阻塞，避免资源耗尽的风险
        ASYNC_UNSAFE//不考虑资源耗尽的问题，无线扩容，常用于测试
    };
    class AsyncLooper
    {
        
    public:
        using ptr=std::shared_ptr<AsyncLooper>;//智能指针类型
        AsyncLooper(const Functor&cb,AsyncType loop_type=AsyncType::ASYNC_SAFE)//异步工作线程得用户告诉我们如何进行去对这个消费者缓冲区中的数据进行处理操作
        :_stop(false)
        //创建了一个新的线程对象,将这个线程对象赋值给成员变量_thread
        //当执行这行代码时:
        //1.会创建一个新的线程对象
        //2、新线程会调用AsyncLooper类的threadEntry()方法
        //3、由于这是一个成员函数，需要通过this指针指定是哪个对象的方法
        //4、新线程独立运行，与主线程并行执行threadEntry()中的代码
        ,_thread(std::thread(&AsyncLooper::threadEntry,this))
        ,_callBack(cb)
        , _looper_type(loop_type)//默认是安全状态
        {};
        ~AsyncLooper()
        {
            stop();//析构函数中调用stop()函数，进行退出操作
        }
        void stop()
        {
            _stop=true;//将退出标志设置为true，说明要进行生产缓冲区停止生产了，然后通知消费者线程进行消费
            //那么我们就得唤醒所有的工作线程了
            _cond_con.notify_all();
            //等待工作线程退出
            _thread.join();
        }
        void push(const char*data,size_t len)//添加数据
        {
            //1、无线扩容--非安全  2、固定大小---生产换中区中数据满了就阻塞
            std::unique_lock<std::mutex> lock(_mutex);//先加个锁
            //条件变量控制，若我们缓冲区剩余空间大小大于数据长度的话，则可以添加数据
            if(_looper_type==AsyncType::ASYNC_SAFE)//如果是安全状态的话，则进行条件变量的等待，如果不是的话，则直接进行添加数据操作
                _cond_pro.wait(lock,[&](){return _pro_buf.writeAbleSize()>=len;});
            //走到这里就说明满足了条件，可以向缓冲区中进行数据的添加操作
            _pro_buf.push(data,len);//缓冲区进行数据插入操作
            //通知消费者线程进行消费
            _cond_con.notify_one();//唤醒消费者进行缓冲区中数据处理
        }
    private:
        //线程入口函数---对消费缓冲区中的数据进行处理，处理完毕后初始化缓冲区，交换缓冲区
        void  threadEntry()
        {
            //stop为true的话就退出循环了
            while(1)//如果没有停止运行的话我们就进行死循环操作，如果停止了的话，就跳出循环了
            {
                {   //1、要判断生产缓冲区中是否有数据，有的话就进行交换操作，无则阻塞
                    std::unique_lock<std::mutex> lock(_mutex);//进行加锁操作

                    //退出标志被设置，并且生产缓冲区已无数据
                    //如果不按照这样的话，否则可能会造成生产缓冲区中有数据，但是没有完全被处理，然后随着析构函数进行析构了
                    if(_stop==true&&_pro_buf.empty())break;//当我要退出了并且当前的生产缓冲区没有数据了，那么我就退出循环了


                    //有生产缓冲区中有数据的话我们就进行交换操作
                    //若当前是退出前被唤醒，或者有数据被唤醒，则返回真，继续向下运行，否则返回假重新进入休眠操作
                    _cond_con.wait(lock,[&](){return _stop|| !_pro_buf.empty()>0;});//等待条件变量，直到生产者缓冲区有数据
                    //如果是退出后被唤醒了的话，那么我们继续进行休眠操作


                    _con_buf.swap(_pro_buf);//交换缓冲区
                    //2、唤醒生产者,交换完毕了就能进行生产者的唤醒操作了    
                    if(_looper_type==AsyncType::ASYNC_SAFE)//如果是安全状态的话就进行唤醒操作
                        _cond_pro.notify_all();//唤醒生产者进行缓冲区中数据处理

                }
                //为互斥锁设置一个生命周期，当交换区交换完毕之后就解锁（并不对数据的处理过程加锁保护）
                //这里我们出了作用域了，那么锁的生命周期就结束了，就自动进行解锁操作，生产者就可以往缓冲区进行数据的填写操作
                
                //3、被唤醒后，对消费缓冲区进行数据处理，
                _callBack(_con_buf);//调用回调函数进行数据处理
                //4、处理完毕后初始化缓冲区
                _con_buf.reset();//重置读写位置，初始化缓冲区

            }
        }

    private:
        Functor _callBack;//具体对缓冲区数据进行处理的回调函数，由异步工作器使用者传入
    private:
        AsyncType _looper_type;//异步工作器的类型，安全还是不安全
        //stop的操作存在线程安全的问题，加锁又没有什么必要，我们直接将这个stop设置为原子的
        std::atomic<bool> _stop;//工作器停止标志---异步工作器是否停止
        Buffer _pro_buf;//生产缓冲区
        Buffer _con_buf;//消费缓冲区
        std::mutex _mutex;
        std::condition_variable _cond_pro;//生产者条件变量
        std::condition_variable _cond_con;//消费者条件变量
        std::thread _thread;//异步工作器对应的工作线程
    };
} 

#endif